package com.sam.designpattern;

import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;

public class Circulation {
    //一级缓存
    private static HashMap<String, Object> singletonMap = new HashMap<String, Object>();
    //bean定义的Map
    private static HashMap<String, RootBeanDefinition> beanDefinitionMap = new HashMap<>();

    public static void main(String[] args) throws Exception {
        // InstanceA和InstanceB两个beanDefinition
        RootBeanDefinition beanDefinitionA = new RootBeanDefinition(InstanceA.class);
        RootBeanDefinition beanDefinitionB = new RootBeanDefinition(InstanceB.class);
        //beanDefinitionMap存放beanDefinition
        beanDefinitionMap.put("instanceA", beanDefinitionA);
        beanDefinitionMap.put("instanceB", beanDefinitionB);
        Object instanceA = getBean("instanceA");
        //((InstanceA)instanceA).hello ();
        ((InstanceB) singletonMap.get("instanceB")).getInstanceA().hello();
    }

    //getBean方法获取bean
    public static Object getBean(String beanName) throws Exception {
        //首先向一级缓存获取bean,如果获取到直接返回
        Object bean = singletonMap.get(beanName);
        if (bean != null) {
            return bean;
        }
        //从beanDefinitionMap中获取对应的beanDefinition
        RootBeanDefinition rootBeanDefinition = beanDefinitionMap.get(beanName);
        //因为是模拟,直接使用了无参构造器获取bean实例,此时bean属性没有赋值
        Class<?> beanClass = rootBeanDefinition.getBeanClass();
        Object instanceBean = beanClass.newInstance();
        Field[] declaredFields = beanClass.getDeclaredFields();
        /*
          这样解决循环依赖,最终得到的InstanceA的成员变量instanceB指向InstanceB而
          InstanceB的成员变量instanceA指向InstanceA形成了环
          为什么解决了循环依赖,就是InstanceB再依赖InstanceA之前先从一级缓存中获取,此时的InstanceA只实例化,没有属性赋值,就是下面这个代码,在没有赋值之前就将InstanceA放到一级缓存中
         */
        singletonMap.put(beanName, instanceBean);
        //遍历属性
        Arrays.stream(declaredFields).forEach(o -> {
            //如果属性标注了Autowired注解,则getBean获取实例    
            Annotation annotation = o.getAnnotation(Autowired.class);
            if (annotation != null) {
                try {
                    Object beanField = getBean(o.getName());
                    //有可能是private
                    o.setAccessible(true);
                    //为属性赋值
                    o.set(instanceBean, beanField);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });

        /*
          把bean放到一级缓存中,这段代码上面也有,如果放在上面可以解决循环依赖,如果不能理解,可以用debug的模式跟代码 就能明白
          如果放在这的话,抛出栈溢出的异常
         */
        singletonMap.put(beanName, instanceBean);
        return instanceBean;
    }
}

//两个循环依赖的类
@Data
class InstanceA {
    @Autowired
    private InstanceB instanceB;

    public void hello() {
        System.out.println("hello");
    }
}

@Data
class InstanceB {
    @Autowired
    private InstanceA instanceA;
}
